﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Xlit.Project.Entity;
using Xlit.Standard.Data;

namespace Xlit.ORM.EfCore
{
    /// <summary>
    ///  仓储实现
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TPrimaryKey"></typeparam>
    /// <typeparam name="TDbContext"></typeparam>
    public partial class EfRepository<TEntity, TPrimaryKey, TDbContext> : IRepository<TEntity, TPrimaryKey>
        where TEntity : Entity<TPrimaryKey>
        where TDbContext : DbContext
    {
        readonly TDbContext Context;

        public EfRepository(TDbContext context)
        {
            Context = context;
        }

        private DbSet<TEntity> DbSet
        {
            get { return Context.Set<TEntity>(); }
        }

        public IQueryable<TEntity> Entities
        {
            get { return DbSet; }
        }
        public int Commit()
        {
            try
            {
                return Context.SaveChanges();
            }
            catch (DbUpdateException ex)
            {
                //var iex = ex.InnerException;
                //if (iex is UpdateException uex)
                //{
                //    var iuex = uex.InnerException;
                //    if (iuex is SqlException sex)
                //    {
                //        if (sex.Number == 2601)
                //        {
                //            // 触发唯一索引错误
                //            throw new Exception("不允许有重复字段值");
                //        }
                //    }
                //}
                throw ex;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public bool IsExist(TPrimaryKey primaryKey)
        {
            return DbSet.Any(d => d.Id.Equals(primaryKey));
        }
        public bool IsExist(TEntity entity)
        {
            return DbSet.Any(d => d == entity);
        }
        public bool IsExist(Expression<Func<TEntity, bool>> predicate)
        {
            return DbSet.Any(predicate);
        }
        public bool IsEmpty()
        {
            return DbSet.Any();
        }
        public long Count()
        {
            return DbSet.Count();
        }
        public long Count(Expression<Func<TEntity, bool>> predicate)
        {
            return DbSet.Count(predicate);
        }

        public TEntity Single(Expression<Func<TEntity, bool>> predicate)
        {
            return DbSet.SingleOrDefault(predicate);
        }
        public TEntity First(Expression<Func<TEntity, bool>> predicate)
        {
            return DbSet.FirstOrDefault(predicate);
        }
        public TEntity GetEntity(TPrimaryKey primaryKey)
        {
            return DbSet.Find(primaryKey);
        }
        public IQueryable<TEntity> GetEntities(Expression<Func<TEntity, bool>> predicate)
        {
            var query = Entities;

            if (predicate != null)
            {
                query = query.Where(predicate);
            }

            return query;
        }
        public IList<TEntity> GetList() { return Entities.ToList(); }
        public IList<TEntity> GetList(Expression<Func<TEntity, bool>> predicate, string include)
        {
            if (!string.IsNullOrWhiteSpace(include))
            {
                return Entities.Include(include).Where(predicate).ToList();
            }
            else
            {
                return Entities.Where(predicate).ToList();
            }
        }

        public int Insert(TEntity entity)
        {
            DbSet.Add(entity);
            return Commit();
        }
        public int Insert(IEnumerable<TEntity> entities)
        {
            DbSet.AddRange(entities);
            return Commit();
        }
        public int InsertOrUpdate(TEntity entity)
        {
            return entity.IsTransient() ?
                 Insert(entity) :
                 Update(entity);
        }
        public int Update(TEntity entity)
        {
            Context.Entry(entity).State = EntityState.Modified;
            return Commit();
        }
        public int Delete(TPrimaryKey primaryKey)
        {
            var entity = GetEntity(primaryKey);
            DbSet.Remove(entity);
            return Commit();
        }
        public int Delete(TEntity entity)
        {
            DbSet.Attach(entity);
            DbSet.Remove(entity);
            return Commit();
        }
        public int Delete(IEnumerable<TEntity> entities)
        {
            DbSet.RemoveRange(entities);
            return Commit();
        }
        public int Delete(Expression<Func<TEntity, bool>> predicate)
        {
            var entities = GetEntities(predicate);
            DbSet.RemoveRange(entities);
            return Commit();
        }
    }

    /// <summary>
    ///  仓储实现 异步
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TPrimaryKey"></typeparam>
    /// <typeparam name="TDbContext"></typeparam>
    public partial class EfRepository<TEntity, TPrimaryKey, TDbContext> : IRepository<TEntity, TPrimaryKey>
        where TEntity : Entity<TPrimaryKey>
        where TDbContext : DbContext
    {
        public async Task<int> CommitAsync()
        {
            return await Context.SaveChangesAsync();
        }

        public async Task<bool> IsExistAsync(TPrimaryKey primaryKey)
        {
            return await DbSet.AnyAsync(d => d.Id.Equals(primaryKey));
        }
        public async Task<bool> IsExistAsync(TEntity entity)
        {
            return await DbSet.AnyAsync(d => d == entity);
        }
        public async Task<bool> IsExistAsync(Expression<Func<TEntity, bool>> predicate)
        {
            return await DbSet.AnyAsync(predicate);
        }
        public async Task<bool> IsEmptyAsync()
        {
            return await DbSet.AnyAsync();
        }
        public async Task<long> CountAsync()
        {
            return await DbSet.CountAsync();
        }
        public async Task<long> CountAsync(Expression<Func<TEntity, bool>> predicate)
        {
            return await DbSet.CountAsync(predicate);
        }

        public async Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate)
        {
            return await DbSet.SingleOrDefaultAsync(predicate);
        }
        public async Task<TEntity> FirstAsync(Expression<Func<TEntity, bool>> predicate)
        {
            return await DbSet.FirstOrDefaultAsync(predicate);
        }
        public async Task<TEntity> GetEntityAsync(TPrimaryKey primaryKey)
        {
            return await DbSet.FindAsync(primaryKey);
        }
        public async Task<IList<TEntity>> GetListAsync()
        {
            return await Entities.ToListAsync();
        }
        public async Task<IList<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate, string include = "")
        {
            if (!string.IsNullOrWhiteSpace(include))
            {
                return await Entities.Include(include).Where(predicate).ToListAsync();
            }
            else
            {
                return await Entities.Where(predicate).ToListAsync();
            }
        }

        public async Task<int> InsertAsync(TEntity entity)
        {
            await DbSet.AddAsync(entity);
            return await CommitAsync();
        }
        public async Task<int> InsertAsync(IEnumerable<TEntity> entities)
        {
            await DbSet.AddRangeAsync(entities);
            return await CommitAsync();
        }
        public async Task<int> InsertOrUpdateAsync(TEntity entity)
        {
            var existing = await Context.FindAsync<TEntity>(entity.Id);
            if (existing == null)
                await DbSet.AddAsync(entity);
            else
                Context.Entry(existing).CurrentValues.SetValues(entity);
            return await CommitAsync();
        }
        public async Task<int> UpdateAsync(TEntity entity)
        {
            Context.Entry(entity).State = EntityState.Modified;
            return await CommitAsync();
        }
        public async Task<int> DeleteAsync(TPrimaryKey primaryKey)
        {
            var entity = await GetEntityAsync(primaryKey);
            DbSet.Remove(entity);
            return await CommitAsync();
        }
        public async Task<int> DeleteAsync(TEntity entity)
        {
            DbSet.Attach(entity);
            DbSet.Remove(entity);
            return await CommitAsync();
        }
        public async Task<int> DeleteAsync(IEnumerable<TEntity> entities)
        {
            DbSet.RemoveRange(entities);
            return await CommitAsync();
        }
        public async Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate)
        {
            var entities = GetEntities(predicate);
            DbSet.RemoveRange(entities);
            return await CommitAsync();
        }
    }
    
    /// <summary>
    ///  仓储实现, 自定义数据源
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TPrimaryKey"></typeparam>
    public class EfRepository<TEntity, TPrimaryKey> : EfRepository<TEntity, TPrimaryKey, DbContext>, IRepository<TEntity, TPrimaryKey>
          where TEntity : Entity<TPrimaryKey>
    {
        public EfRepository(DbContext context) : base(context)
        {
        }
    }
}
